function stable_svm_training()
% 稳定的SVM训练脚本 - 确保训练结果的一致性和高准确率
% 解决SVM识别准确率不稳定的问题

fprintf('===== 稳定SVM训练系统 =====\n');

% 设置随机种子，确保结果可重现
rng(42, 'twister');  % 固定随机种子
fprintf('✓ 已设置固定随机种子，确保结果可重现\n');

% 配置参数
config = struct();
config.trainFolder = 'D:\文档资料\OneDrive\桌面\人脸配准(眉毛特征点)\train';
config.trainImgPerPerson = 5;
config.pcaRatio = 0.95;
config.imgSize = 100;  % 图像尺寸标准化

% SVM稳定参数配置
config.svm = struct();
config.svm.kernelFunction = 'rbf';
config.svm.kernelScale = 1.0;      % 固定核尺度，不使用'auto'
config.svm.boxConstraint = 10;     % 稍微增加约束参数
config.svm.standardize = true;     % 标准化特征
config.svm.crossValidation = true; % 使用交叉验证优化参数

fprintf('训练配置:\n');
fprintf('- 训练文件夹: %s\n', config.trainFolder);
fprintf('- 每人训练图像数: %d\n', config.trainImgPerPerson);
fprintf('- PCA贡献率: %.2f\n', config.pcaRatio);
fprintf('- SVM核函数: %s\n', config.svm.kernelFunction);
fprintf('- SVM核尺度: %.1f (固定值)\n', config.svm.kernelScale);
fprintf('- SVM约束参数: %d\n', config.svm.boxConstraint);
fprintf('\n');

% 检查训练文件夹
if ~exist(config.trainFolder, 'dir')
    error('训练文件夹不存在: %s', config.trainFolder);
end

% 1. 稳定的数据加载
fprintf('【步骤1/6】稳定加载训练数据...\n');
[trainFaces, trainLabels, fileInfo] = loadTrainingDataStable(config);
fprintf('✓ 加载完成: %d个人，共%d张图像\n', length(unique(trainLabels)), length(trainLabels));

% 2. 数据预处理和标准化
fprintf('【步骤2/6】数据预处理和标准化...\n');
[trainFaces, meanFace] = preprocessDataStable(trainFaces);
fprintf('✓ 数据标准化完成\n');

% 3. 稳定的PCA降维
fprintf('【步骤3/6】稳定PCA降维...\n');
[eigenFaces, projectMatrix, weightedTrain] = performStablePCA(trainFaces, meanFace, config.pcaRatio);
fprintf('✓ PCA降维完成: %d -> %d 维\n', size(trainFaces, 1), size(eigenFaces, 2));

% 4. 优化的SVM训练
fprintf('【步骤4/6】优化SVM训练...\n');
svmModel = trainStableSVM(weightedTrain', trainLabels, config.svm);
fprintf('✓ SVM训练完成\n');

% 5. 训练集自测试
fprintf('【步骤5/6】训练集自测试...\n');
trainAccuracy = testSVMAccuracy(svmModel, weightedTrain', trainLabels);
fprintf('✓ 训练集准确率: %.2f%%\n', trainAccuracy);

% 6. 交叉验证测试
fprintf('【步骤6/6】交叉验证测试...\n');
cvAccuracy = performCrossValidation(weightedTrain', trainLabels, config.svm);
fprintf('✓ 交叉验证准确率: %.2f%%\n', cvAccuracy);

% 保存稳定模型
fprintf('\n保存稳定训练模型...\n');
stableModel = struct();
stableModel.svmModel = svmModel;
stableModel.eigenFaces = eigenFaces;
stableModel.projectMatrix = projectMatrix;
stableModel.meanFace = meanFace;
stableModel.trainLabels = trainLabels;
stableModel.config = config;
stableModel.trainAccuracy = trainAccuracy;
stableModel.cvAccuracy = cvAccuracy;
stableModel.trainingTimestamp = datestr(now);

save('stable_svm_model.mat', 'stableModel');
fprintf('✓ 模型已保存到: stable_svm_model.mat\n');

% 输出稳定性保证措施
fprintf('\n===== 稳定性保证措施 =====\n');
fprintf('1. ✓ 固定随机种子 (rng(42))\n');
fprintf('2. ✓ 按文件名排序加载数据\n');
fprintf('3. ✓ 固定SVM参数，不使用自动调优\n');
fprintf('4. ✓ 标准化特征向量\n');
fprintf('5. ✓ 稳定的PCA降维（固定特征向量符号）\n');
fprintf('6. ✓ 交叉验证确保模型泛化能力\n');

if trainAccuracy >= 99.0 && cvAccuracy >= 95.0
    fprintf('\n🎉 成功！模型达到高稳定性要求！\n');
    fprintf('   训练准确率: %.2f%% (≥99%%)\n', trainAccuracy);
    fprintf('   交叉验证准确率: %.2f%% (≥95%%)\n', cvAccuracy);
else
    fprintf('\n⚠️  模型准确率未达到最佳要求\n');
    fprintf('   建议调整参数或增加训练数据\n');
end

fprintf('\n===== 稳定SVM训练完成 =====\n');

end

function [trainFaces, trainLabels, fileInfo] = loadTrainingDataStable(config)
% 稳定加载训练数据，确保每次加载顺序一致

% 获取所有图像文件并排序
imgExtensions = {'.jpg', '.png', '.bmp', '.jpeg', '.tif'};
allFiles = [];

for i = 1:length(imgExtensions)
    pattern = ['*' imgExtensions{i}];
    files = dir(fullfile(config.trainFolder, pattern));
    allFiles = [allFiles; files];
end

if isempty(allFiles)
    error('训练文件夹中未找到图像文件');
end

% 按文件名排序，确保加载顺序一致
[~, sortIdx] = sort({allFiles.name});
allFiles = allFiles(sortIdx);

% 解析文件名并按人物ID分组
personData = containers.Map('KeyType', 'int32', 'ValueType', 'any');

for i = 1:length(allFiles)
    filename = allFiles(i).name;
    tokens = regexp(filename, '(\d+)[-_](\d+)', 'tokens');
    
    if ~isempty(tokens)
        personID = str2double(tokens{1}{1});
        imageSeq = str2double(tokens{1}{2});
        
        if isKey(personData, personID)
            data = personData(personID);
            data.sequences(end+1) = imageSeq;
            data.files(end+1) = i;
            personData(personID) = data;
        else
            data = struct();
            data.sequences = imageSeq;
            data.files = i;
            personData(personID) = data;
        end
    end
end

% 按人物ID排序并稳定选择训练图像
personIDs = sort(cell2mat(keys(personData)));
trainFaces = [];
trainLabels = [];
fileInfo = [];

for i = 1:length(personIDs)
    personID = personIDs(i);
    data = personData(personID);
    
    % 按序号排序，确保选择的一致性
    [sortedSeqs, sortIdx] = sort(data.sequences);
    sortedFiles = data.files(sortIdx);
    
    % 选择前N张图像
    numToUse = min(config.trainImgPerPerson, length(sortedFiles));
    selectedFiles = sortedFiles(1:numToUse);
    
    % 加载图像
    for j = 1:length(selectedFiles)
        fileIdx = selectedFiles(j);
        imgPath = fullfile(config.trainFolder, allFiles(fileIdx).name);
        
        img = imread(imgPath);
        if size(img, 3) == 3
            img = rgb2gray(img);
        end
        
        % 标准化图像尺寸
        img = imresize(img, [config.imgSize, config.imgSize]);
        imgVector = double(img(:));
        
        trainFaces = [trainFaces, imgVector];
        trainLabels = [trainLabels; personID];
        fileInfo = [fileInfo; struct('personID', personID, 'filename', allFiles(fileIdx).name)];
    end
end

end

function [trainFaces, meanFace] = preprocessDataStable(trainFaces)
% 稳定的数据预处理

% 计算均值并中心化
meanFace = mean(trainFaces, 2);
trainFaces = trainFaces - repmat(meanFace, 1, size(trainFaces, 2));

% L2标准化
for i = 1:size(trainFaces, 2)
    norm_val = norm(trainFaces(:, i));
    if norm_val > 0
        trainFaces(:, i) = trainFaces(:, i) / norm_val;
    end
end

end

function [eigenFaces, projectMatrix, weightedTrain] = performStablePCA(trainFaces, meanFace, pcaRatio)
% 稳定的PCA降维，确保特征向量方向一致

% 计算协方差矩阵
C = trainFaces' * trainFaces;
[V, D] = eig(C);

% 按特征值排序
eigenValues = diag(D);
[eigenValues, sortIdx] = sort(eigenValues, 'descend');
V = V(:, sortIdx);

% 选择主成分
cumSum = cumsum(eigenValues) / sum(eigenValues);
numComponents = find(cumSum >= pcaRatio, 1);
if isempty(numComponents)
    numComponents = length(eigenValues);
end

selectedV = V(:, 1:numComponents);

% 计算特征脸（确保方向一致性）
eigenFaces = trainFaces * selectedV;

% 标准化特征脸并确保符号一致
for i = 1:size(eigenFaces, 2)
    eigenFace = eigenFaces(:, i);
    % 确保特征脸的第一个非零元素为正，保证符号一致性
    firstNonZero = find(abs(eigenFace) > 1e-10, 1);
    if ~isempty(firstNonZero) && eigenFace(firstNonZero) < 0
        eigenFaces(:, i) = -eigenFace;
        selectedV(:, i) = -selectedV(:, i);
    end
    
    % 标准化
    norm_val = norm(eigenFaces(:, i));
    if norm_val > 0
        eigenFaces(:, i) = eigenFaces(:, i) / norm_val;
    end
end

% 投影矩阵
projectMatrix = eigenFaces;

% 投影训练数据
weightedTrain = projectMatrix' * trainFaces;

end

function svmModel = trainStableSVM(X_train, Y_train, svmConfig)
% 稳定的SVM训练

if svmConfig.crossValidation
    % 使用交叉验证找到最优参数
    fprintf('    使用交叉验证优化SVM参数...\n');
    
    % 参数搜索范围
    kernelScales = [0.1, 0.5, 1.0, 2.0, 5.0];
    boxConstraints = [0.1, 1, 10, 100];
    
    bestAccuracy = 0;
    bestParams = struct();
    
    for ks = kernelScales
        for bc = boxConstraints
            % 5折交叉验证
            cv = cvpartition(Y_train, 'KFold', 5);
            accuracies = [];
            
            for fold = 1:5
                trainIdx = training(cv, fold);
                testIdx = test(cv, fold);
                
                tempModel = fitcecoc(X_train(trainIdx, :), Y_train(trainIdx), ...
                    'Learners', templateSVM('KernelFunction', svmConfig.kernelFunction, ...
                                           'KernelScale', ks, ...
                                           'BoxConstraint', bc, ...
                                           'Standardize', svmConfig.standardize), ...
                    'Verbose', 0);
                
                predictions = predict(tempModel, X_train(testIdx, :));
                accuracy = mean(predictions == Y_train(testIdx)) * 100;
                accuracies(end+1) = accuracy;
            end
            
            avgAccuracy = mean(accuracies);
            if avgAccuracy > bestAccuracy
                bestAccuracy = avgAccuracy;
                bestParams.kernelScale = ks;
                bestParams.boxConstraint = bc;
            end
        end
    end
    
    fprintf('    最优参数: KernelScale=%.1f, BoxConstraint=%.1f\n', ...
            bestParams.kernelScale, bestParams.boxConstraint);
    fprintf('    交叉验证准确率: %.2f%%\n', bestAccuracy);
    
    % 使用最优参数训练最终模型
    svmModel = fitcecoc(X_train, Y_train, ...
        'Learners', templateSVM('KernelFunction', svmConfig.kernelFunction, ...
                               'KernelScale', bestParams.kernelScale, ...
                               'BoxConstraint', bestParams.boxConstraint, ...
                               'Standardize', svmConfig.standardize), ...
        'Verbose', 0);
else
    % 使用固定参数
    svmModel = fitcecoc(X_train, Y_train, ...
        'Learners', templateSVM('KernelFunction', svmConfig.kernelFunction, ...
                               'KernelScale', svmConfig.kernelScale, ...
                               'BoxConstraint', svmConfig.boxConstraint, ...
                               'Standardize', svmConfig.standardize), ...
        'Verbose', 0);
end

end

function accuracy = testSVMAccuracy(svmModel, X_test, Y_test)
% 测试SVM模型准确率

predictions = predict(svmModel, X_test);
accuracy = mean(predictions == Y_test) * 100;

end

function cvAccuracy = performCrossValidation(X, Y, svmConfig)
% 执行交叉验证

cv = cvpartition(Y, 'KFold', 5);
accuracies = [];

for fold = 1:5
    trainIdx = training(cv, fold);
    testIdx = test(cv, fold);
    
    model = fitcecoc(X(trainIdx, :), Y(trainIdx), ...
        'Learners', templateSVM('KernelFunction', svmConfig.kernelFunction, ...
                               'KernelScale', svmConfig.kernelScale, ...
                               'BoxConstraint', svmConfig.boxConstraint, ...
                               'Standardize', svmConfig.standardize), ...
        'Verbose', 0);
    
    predictions = predict(model, X(testIdx, :));
    accuracy = mean(predictions == Y(testIdx)) * 100;
    accuracies(end+1) = accuracy;
end

cvAccuracy = mean(accuracies);

end 